package com.bbkb.quartz.config.log;

import com.aliyun.openservices.log.common.LogItem;
import com.aliyun.openservices.log.producer.LogProducer;
import com.aliyun.openservices.log.producer.ProducerConfig;
import com.aliyun.openservices.log.producer.ProjectConfig;
import com.bbkb.common.constants.LogConstants;
import com.bbkb.common.log.Log;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 功能:
 * 作者:沈飞超
 * 日期:2021-05-28 16:02
 */
public class LogHubAppender extends AppenderSkeleton {
    //日志生产者配置
    private final ProducerConfig config = new ProducerConfig();
    //远程日志项目配置
    private final ProjectConfig projectConfig = new ProjectConfig();
    //日志生产者
    private LogProducer producer;
    //远程日志模块名，例如，bbkb_manager
    private String logstore;
    //主题
    private String topic = "";
    //项目名称
    private String logAppName;

    /**
     * 初始化资源
     */
    @Override
    public void activateOptions() {
        super.activateOptions();
        //初始化日志生产者以及配置
        this.producer = new LogProducer(this.config);
        this.producer.setProjectConfig(this.projectConfig);
    }

    /**
     * 关闭流
     */
    @Override
    public void close() {
        this.producer.flush();
        this.producer.close();
    }

    @Override
    public boolean requiresLayout() {
        return false;
    }


    @Override
    protected void append(LoggingEvent event) {
        List<LogItem> logItems = new ArrayList<>();
        Object message = event.getMessage();
        if (message instanceof Log) {

        } else {
            LogItem logItem = getLogItem(event, message.toString());
            logItems.add(logItem);
        }
        producer.send(projectConfig.projectName, logstore, topic, null, logItems);
    }

    private LogItem getLogItem(LoggingEvent event, String message) {
        LogItem logItem = new LogItem();
        //设置请求标识
        Map<String, String> requestThreadLocalMap = LogConstants.requestThreadLocal.get();
        String requestToken = requestThreadLocalMap.get(LogConstants.REQUEST_TOKEN_KEY);
        requestToken = StringUtils.isBlank(requestToken) ? LogConstants.REQUEST_TOKEN_DEFAULT : requestToken;
        logItem.PushBack(LogConstants.REQUEST_TOKEN_KEY, requestToken);
        //异常信息
        String throwableMsg = "";
        ThrowableInformation throwableInformation = event.getThrowableInformation();
        LocationInfo locationInfo = event.getLocationInformation();
        if (throwableInformation != null) {
            Throwable throwable = throwableInformation.getThrowable();
            if (throwable != null) {
                throwableMsg = Arrays.toString(throwableInformation.getThrowableStrRep());
            }
        }
        if (logAppName != null) {
            logItem.PushBack("logAppName", logAppName);
        }
        logItem.PushBack("logType", event.getLevel().toString());
        logItem.PushBack("className", event.getLoggerName());
        logItem.PushBack("methodName", locationInfo.getMethodName());
        logItem.PushBack("parameter", "(line:" + locationInfo.getLineNumber() + ")" + message);
        logItem.PushBack("threadName", event.getThreadName());
        if (StringUtils.isNotBlank(throwableMsg)) {
            logItem.PushBack("throwableMsg", throwableMsg);
        }
        return logItem;
    }

    public String getLogstore() {
        return this.logstore;
    }

    public void setLogstore(String logstore) {
        this.logstore = logstore;
    }

    public String getTopic() {
        return this.topic;
    }

    public void setTopic(String topic) {
        this.topic = topic;
    }

    public String getProjectName() {
        return this.projectConfig.projectName;
    }

    public void setProjectName(String projectName) {
        this.projectConfig.projectName = projectName;
    }

    public String getEndpoint() {
        return this.projectConfig.endpoint;
    }

    public void setEndpoint(String endpoint) {
        this.projectConfig.endpoint = endpoint;
    }

    public String getAccessKeyId() {
        return this.projectConfig.accessKeyId;
    }

    public void setAccessKeyId(String accessKeyId) {
        this.projectConfig.accessKeyId = accessKeyId;
    }

    public String getAccessKey() {
        return this.projectConfig.accessKey;
    }

    public void setAccessKey(String accessKey) {
        this.projectConfig.accessKey = accessKey;
    }

    public String getStsToken() {
        return this.projectConfig.stsToken;
    }

    public void setStsToken(String stsToken) {
        this.projectConfig.stsToken = stsToken;
    }

    public int getPackageTimeoutInMS() {
        return this.config.packageTimeoutInMS;
    }

    public void setPackageTimeoutInMS(int packageTimeoutInMS) {
        this.config.packageTimeoutInMS = packageTimeoutInMS;
    }

    public int getLogsCountPerPackage() {
        return this.config.logsCountPerPackage;
    }

    public void setLogsCountPerPackage(int logsCountPerPackage) {
        this.config.logsCountPerPackage = logsCountPerPackage;
    }

    public int getLogsBytesPerPackage() {
        return this.config.logsBytesPerPackage;
    }

    public void setLogsBytesPerPackage(int logsBytesPerPackage) {
        this.config.logsBytesPerPackage = logsBytesPerPackage;
    }

    public int getMemPoolSizeInByte() {
        return this.config.memPoolSizeInByte;
    }

    public void setMemPoolSizeInByte(int memPoolSizeInByte) {
        this.config.memPoolSizeInByte = memPoolSizeInByte;
    }

    public int getIoThreadsCount() {
        return this.config.maxIOThreadSizeInPool;
    }

    public void setIoThreadsCount(int ioThreadsCount) {
        this.config.maxIOThreadSizeInPool = ioThreadsCount;
    }

    public int getShardHashUpdateIntervalInMS() {
        return this.config.shardHashUpdateIntervalInMS;
    }

    public void setShardHashUpdateIntervalInMS(int shardHashUpdateIntervalInMS) {
        this.config.shardHashUpdateIntervalInMS = shardHashUpdateIntervalInMS;
    }

    public int getRetryTimes() {
        return this.config.retryTimes;
    }

    public void setRetryTimes(int retryTimes) {
        this.config.retryTimes = retryTimes;
    }

    public String getLogAppName() {
        return logAppName;
    }

    public void setLogAppName(String logAppName) {
        this.logAppName = logAppName;
    }
}
